﻿using Tszy.Unlimited.Base.Collections;

namespace Tszy.Unlimited.Base.Infrastructure.Caching;

/// <summary>
/// 默认缓存管理器。
/// </summary>
public class DefaultCacheManager : ICacheManager
{
    /// <summary>
    /// 静态字典缓存数据。
    /// </summary>
    private readonly MonoConcurrentDictionary<string, CacheEntity> _cache = new();

    /// <summary>
    /// 锁定对象。
    /// </summary>
    private static readonly object _lockObject = new();

    /// <summary>
    /// 获得所有 Key。
    /// </summary>
    public IEnumerable<string> Keys => _cache.Keys;

    /// <summary>
    /// 增加缓存。
    /// </summary>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public bool Add<TValue>(string key, TValue value)
    {
        lock (_lockObject)
        {
            if (_cache.ContainsKey(key))
                return false;

            Set(key, value);

            return true;
        }
    }

    /// <summary>
    /// 获取缓存值。
    /// </summary>
    /// <typeparam name="TValue">值类型</typeparam>
    /// <param name="key">键对象</param>
    /// <returns>值对象</returns>
    public TValue? Get<TValue>(string key)
    {
        var cache = GetEntity(key);

        if (cache             != null &&
            cache.Value       != null &&
            cache.ExpiredTime > DateTime.Now)
        {
            return (TValue)cache.Value;
        }

        return default;
    }

    private CacheEntity? GetEntity(string key)
    {
        CacheEntity? cache = null;

        lock (_lockObject)
        {
            if (_cache.ContainsKey(key))
                cache = _cache[key];
        }

        return cache;
    }

    /// <summary>
    /// 获取缓存值，如果不存在，则添加缓存。
    /// </summary>
    /// <typeparam name="TValue">值类型</typeparam>
    /// <param name="key">键对象</param>
    /// <param name="acquire">获得值对象的委托。</param>
    /// <returns>值对象。</returns>
    public TValue GetOrAdd<TValue>(string key, Func<TValue> acquire)
    {
        lock (_lockObject)
        {
            var result = Get<TValue>(key);

            if (result != null)
                return result;

            result = acquire();

            if (result != null)
            {
                Set(key, result, TimeSpan.FromMinutes(20));
            }

            return result;
        }
    }

    /// <summary>
    /// 获取缓存值，如果不存在，则添加缓存。
    /// </summary>
    /// <typeparam name="TValue">值类型。</typeparam>
    /// <param name="key">键对象。</param>
    /// <param name="acquire">获得值对象的委托。</param>
    /// <param name="validFor"></param>
    /// <returns>值对象。</returns>
    public TValue GetOrAdd<TValue>(string key, Func<TValue> acquire, TimeSpan validFor)
    {
        lock (_lockObject)
        {
            var result = Get<TValue>(key);

            if (result != null)
                return result;

            result = acquire();

            if (result != null)
            {
                Set(key, result, validFor);
            }

            return result;
        }
    }

    /// <summary>
    /// 增加缓存值。
    /// </summary>
    /// <typeparam name="TValue">值类型</typeparam>
    /// <param name="key">键对象</param>
    /// <param name="value">值对象</param>
    public void Set<TValue>(string key, TValue value)
    {
        if (string.IsNullOrWhiteSpace(key))
            throw new ArgumentNullException(nameof(key), "键对象不可以是空的。");

        Set(key, value, TimeSpan.FromMinutes(20));
    }

    /// <summary>
    /// 增加缓存值。
    /// </summary>
    /// <typeparam name="TValue">值类型。</typeparam>
    /// <param name="key">键对象。</param>
    /// <param name="value">值对象。</param>
    /// <param name="validFor">有效时长。</param>
    /// <exception cref="ArgumentNullException">key 不可以是空的。</exception>
    public void Set<TValue>(string key, TValue value, TimeSpan validFor)
    {
        if (string.IsNullOrWhiteSpace(key))
            throw new ArgumentNullException(nameof(key), "键对象不可以是空的。");

        if (value == null)
            throw new ArgumentNullException(nameof(value), "值对象不可以是空的。");

        var created = DateTime.Now;

        var cache = new CacheEntity
                    {
                        Value       = value,
                        ExpiredTime = created.Add(validFor),
                        Created     = created,
                    };

        lock (_lockObject)
        {
            ExpiredClear();

            if (_cache.ContainsKey(key))
            {
                _cache[key] = cache;
            }
            else
            {
                _cache.Add(key, cache);
            }
        }
    }

    /// <summary>
    /// 使用并发锁设置缓存值。
    /// </summary>
    /// <typeparam name="TValue">值类型。</typeparam>
    /// <param name="key">键对象。</param>
    /// <param name="action">缓存值修改方法。</param>
    /// <param name="timeOut">缓存的有效时长。</param>
    /// <param name="lockTimeOut">锁的有效时长。</param>
    public void SetLock<TValue>(string key, Func<TValue, TValue> action, TimeSpan timeOut, TimeSpan lockTimeOut)
    {
        if (string.IsNullOrWhiteSpace(key))
            throw new ArgumentNullException(nameof(key), "键对象不可以是空的");

        if (action == null)
            throw new ArgumentNullException(nameof(action), "设置方法不可以是空的");

        lock (_lockObject)
        {
            var value = Get<TValue>(key);

            if (value == null)
                throw new ArgumentNullException(nameof(value), "值对象不可以是空的。");

            value = action(value);

            Set(key, value, timeOut);
        }
    }

    /// <summary>
    /// 为给定 key 设置过期时间。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="timeOut">时间间隔。</param>
    /// <returns></returns>
    public bool Expire(string key, TimeSpan timeOut)
    {
        var cache = GetEntity(key);

        if (cache == null)
            return false;

        cache.ExpiredTime = cache.Created.Add(timeOut);

        return true;
    }

    /// <summary>
    /// 为给定 key 设置过期时间。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="timeOut">过期时间。</param>
    /// <returns></returns>
    public bool ExpireAt(string key, DateTime timeOut)
    {
        var cache = GetEntity(key);

        if (cache == null)
            return false;

        cache.ExpiredTime = timeOut;

        return true;
    }

    /// <summary>
    /// 移除缓存值。
    /// </summary>
    /// <param name="key">键对象。</param>
    public bool Remove(string key)
    {
        lock (_lockObject)
        {
            return _cache.Remove(key);
        }
    }

    /// <summary>
    /// 移除缓存值。
    /// </summary>
    /// <typeparam name="TValue">值类型</typeparam>
    /// <param name="key">键对象</param>
    /// <param name="result">值对象</param>
    /// <returns></returns>
    public bool Remove<TValue>(string key, out TValue? result)
    {
        lock (_lockObject)
        {
            result = Get<TValue>(key);

            if (_cache.ContainsKey(key))
            {
                return _cache.Remove(key);
            }

            return false;
        }
    }

    /// <summary>
    /// 清空缓存。
    /// </summary>
    public void Clear()
    {
        _cache.Clear();
    }

    private void ExpiredClear()
    {
        if (_cache == null)
        {
            return;
        }

        if (_cache.Count <= 0)
        {
            return;
        }

        var keys = new List<string>();

        foreach (var cache in _cache)
        {
            if (cache.Value.ExpiredTime > DateTime.Now)
            {
                continue;
            }

            keys.Add(cache.Key);
        }

        foreach (var key in keys)
        {
            _cache.Remove(key);
        }

        //var cacheKeys = _cache
        //    .Where(item => item.Value.ExpiredTime < DateTime.Now)
        //    .Select(item => item.Key);

        //if (cacheKeys.Any())
        //{
        //    foreach (var cacheKey in cacheKeys)
        //    {
        //        _cache.Remove(cacheKey);
        //    }
        //}
    }

    #region 数值操作

    /// <summary>
    /// 根据指定的Key，将值加1(仅整型有效)。
    /// </summary>
    /// <param name="key"></param>
    public void IncrementValue(string key)
    {
        lock (_lockObject)
        {
            if (!_cache.ContainsKey(key))
            {
                Set(key, 1);
            }
            else
            {
                var cache = _cache[key];

                if (cache       != null &&
                    cache.Value != null)
                {
                    var value = Convert.ToInt32(cache.Value);

                    Set(key, ++value);
                }
            }
        }
    }

    /// <summary>
    /// 根据指定的Key，将值加上指定值(仅整型有效)。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public void IncrementValueBy(string key, int value)
    {
        lock (_lockObject)
        {
            if (!_cache.ContainsKey(key))
            {
                Set(key, value);
            }
            else
            {
                var cache = _cache[key];

                if (cache       != null &&
                    cache.Value != null)
                {
                    cache.Value = Convert.ToInt32(cache.Value) + value;

                    Set(key, cache.Value);
                }
            }
        }
    }

    /// <summary>
    /// 根据指定的Key，将值减1(仅整型有效)。
    /// </summary>
    /// <param name="key"></param>
    public void DecrementValue(string key)
    {
        lock (_lockObject)
        {
            if (!_cache.ContainsKey(key))
            {
                Set(key, -1);
            }
            else
            {
                var cache = _cache[key];

                if (cache       != null &&
                    cache.Value != null)
                {
                    var value = Convert.ToInt32(cache.Value);

                    Set(key, --value);
                }
            }
        }
    }

    /// <summary>
    /// 根据指定的Key，将值减去指定值(仅整型有效)。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public void DecrementValueBy(string key, int value)
    {
        lock (_lockObject)
        {
            if (!_cache.ContainsKey(key))
            {
                Set(key, -value);
            }
            else
            {
                var cache = _cache[key];

                if (cache       != null &&
                    cache.Value != null)
                {
                    cache.Value = Convert.ToInt32(cache.Value) - value;

                    Set(key, cache.Value);
                }
            }
        }
    }

    #endregion 数值操作

    #region 集合操作

    /// <summary>
    /// 添加一个项到内部的List。
    /// </summary>
    /// <param name="listId"></param>
    /// <param name="value"></param>
    public void AddItemToList(string listId, string value)
    {
        lock (_lockObject)
        {
            var list = new List<string>();

            if (!_cache.ContainsKey(listId))
            {
                list.Add(value);

                Set(listId, list);
            }
            else
            {
                var cache = _cache[listId];

                if (cache       != null &&
                    cache.Value != null)
                {
                    list = (List<string>)cache.Value;

                    list.Add(value);

                    Set(listId, list);
                }
            }
        }
    }

    /// <summary>
    /// 一次过将参数中的List中的多个值添加入内部的List。
    /// </summary>
    /// <param name="listId"></param>
    /// <param name="value"></param>
    public void AddRangeToList(string listId, IEnumerable<string> value)
    {
        lock (_lockObject)
        {
            var list = new List<string>();

            if (!_cache.ContainsKey(listId))
            {
                list.AddRange(value);

                Set(listId, list);
            }
            else
            {
                var cache = _cache[listId];

                if (cache       != null &&
                    cache.Value != null)
                {
                    list = (List<string>)cache.Value;

                    list.AddRange(value);

                    Set(listId, list);
                }
            }
        }
    }

    /// <summary>
    /// 移除指定ListId的内部List中第二个参数值相等的那一项。
    /// </summary>
    /// <param name="listId"></param>
    /// <param name="value"></param>
    public void RemoveItemFromList(string listId, string value)
    {
        lock (_lockObject)
        {
            if (_cache.ContainsKey(listId))
            {
                var cache = _cache[listId];

                if (cache       != null &&
                    cache.Value != null)
                {
                    var list = (List<string>)cache.Value;

                    var status = list.Remove(value);

                    if (status)
                    {
                        Set(listId, list);
                    }
                }
            }
        }
    }

    /// <summary>
    /// 获取指定ListId的内部List的所有值。
    /// </summary>
    /// <param name="listId"></param>
    /// <returns></returns>
    public IEnumerable<string> GetAllItemsFromList(string listId)
    {
        lock (_lockObject)
        {
            if (_cache.ContainsKey(listId))
            {
                var cache = _cache[listId];

                if (cache       != null &&
                    cache.Value != null)
                {
                    return (List<string>)cache.Value;
                }
            }

            return Enumerable.Empty<string>();
        }
    }

    #endregion 集合操作

    /// <summary>
    /// 缓存实体。
    /// </summary>
    private class CacheEntity
    {
        public CacheEntity() { }

        /// <summary>
        /// 缓存值。
        /// </summary>
        public object? Value { get; set; }

        /// <summary>
        /// 过期时间（UTC）
        /// </summary>
        public DateTime ExpiredTime { get; set; }

        /// <summary>
        /// 创建时间。
        /// </summary>
        public DateTime Created { get; set; }
    }

    /// <summary>
    /// 参与者状态。
    /// </summary>
    private struct EventStatus
    {
        /// <summary>
        /// 空闲。
        /// </summary>
        public const int Idle = 0;

        /// <summary>
        /// 忙碌。
        /// </summary>
        public const int Busy = 1;

        /// <summary>
        /// 退出。
        /// </summary>
        public const int Exit = 2;
    }
}